package cn.enited.devtools.apiaggregator.service.impl;

import cn.enited.devtools.apiaggregator.consts.CommonConst;
import cn.enited.devtools.apiaggregator.repo.BaseRepository;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.enited.devtools.apiaggregator.consts.MatchRegexPolicy;
import cn.enited.devtools.apiaggregator.entity.BaseEntity;
import cn.enited.devtools.apiaggregator.service.BaseService;
import cn.enited.devtools.apiaggregator.utils.NumUtil;
import cn.enited.devtools.apiaggregator.vo.ParamVo;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.support.PageableExecutionUtils;

import java.io.Serializable;
import java.util.List;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;

abstract class AbstractService<T extends BaseRepository, E extends BaseEntity> implements BaseService<T, E> {

    public abstract T getRepo();

    Query buildQuery(ParamVo param, Function<ParamVo, Criteria> criteriaBuilder) {
        Criteria criteria = criteriaBuilder.apply(param);
        Query query = Query.query(criteria);
        fillSortableIntoQuery(query, param);
        fileLimitIntoQuery(query, param);
        return query;
    }

    private void fillSortableIntoQuery(Query query, ParamVo param) {
        if (MapUtil.isNotEmpty(param.getSort())) {
            List<Sort.Order> orders = param.getSort().entrySet().stream()
                    .map(entry -> CommonConst.SORT.DESC == entry.getValue()
                            ? Sort.Order.desc(entry.getKey()) : Sort.Order.by(entry.getKey()))
                    .collect(Collectors.toList());
            query.with(Sort.by(orders));
        }
    }

    private void fileLimitIntoQuery(Query query, ParamVo param) {
        if (NumUtil.isValidSize(param.getLimit())) {
            query.limit(param.getLimit());
        }
    }

    <D extends Serializable> Page<D> convertEntityPageToDtoPage(Page<E> entityPage, Supplier<List<D>> dtoListSupplier) {
        return PageableExecutionUtils.getPage(dtoListSupplier.get(), entityPage.getPageable(), entityPage::getTotalElements);
    }

    String buildMatchRegex(String value, MatchRegexPolicy matchRegexPolicy) {
        return StrUtil.format(matchRegexPolicy.pattern(), value);
    }
}
